﻿using System;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Runtime;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;

using PickGold;
using PickGold.Api;
using PickGold.Base;
using PickGold.Collections;

using BaseCommon = PickGold.Base.Common;

namespace PickGold.Base.OCR
{
	[ComVisible(true)]
	[ClassInterface(ClassInterfaceType.AutoDispatch)]
	public class COcr
	{
		private static COcr _Value;

		public static COcr Value
		{
			get
			{
				if (COcr._Value == null)
					COcr._Value = new COcr();
				return COcr._Value;
			}
		}

		public object Tag;

		public event System.EventHandler<OcrEventArgs> MatchText;

		public void OnMatchText(OcrEventArgs e)
		{
			if (this.MatchText != null)
			{
				this.MatchText(this, e);
				return;
			}

			return;
		}

		public bool[,] GetTextPixels(Bitmap bitmap, ColorEquals[] ces)
		{
			return this.GetTextPixels(bitmap, ces, Rectangle.Empty);
		}
		public bool[,] GetTextPixels(Bitmap bitmap, ColorEquals[] ces, Rectangle area)
		{
			if (area == Rectangle.Empty)
				area = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
			var iX = area.X;
			var iY = area.Y;
			var iR = area.Right;
			var iB = area.Bottom;
			var iW = area.Width;
			var iH = area.Height;
			var pixels = new bool[iW, iH];
			for (var x = iX; x < iR; x++)
			{
				for (var y = iY; y < iB; y++)
					pixels[x - iX, y - iY] = ColorEquals.Equals(ces, bitmap.GetPixel(x, y));
			}
			return pixels;
		}

		public bool[,] GetTextTrim(bool[,] pixels)
		{
			return this.GetTextTrim(Rectangle.Empty, pixels);
		}
		public bool[,] GetTextTrim(Rectangle area, bool[,] pixels)
		{
			area = this.GetTextArea(area, pixels);
			if (area == Rectangle.Empty || area.Width <= 0 || area.Height <= 0)
				return null;

			int i = 0;
			var bs = new bool[area.Width, area.Height];
			for (var x = area.Left; x < area.Right; x++)
			{
				for (var y = area.Top; y < area.Bottom; y++)
				{
					if (pixels[x, y])
						i++;
					bs[x - area.Left, y - area.Top] = pixels[x, y];
				}
			}
			if (i == 0)
				return null;

			return bs;
		}

		#region SetTextBackground

		public void SetTextBackground(Bitmap bitmap, Color color, ColorEquals[] ces)
		{
			this.SetTextBackground(bitmap, color, this.GetTextPixels(bitmap, ces));
		}
		public void SetTextBackground(Bitmap bitmap, Color color, bool[,] pixels)
		{
			for (var x = 0; x < bitmap.Width; x++)
			{
				for (var y = 0; y < bitmap.Height; y++)
				{
					if (!pixels[x, y])
						bitmap.SetPixel(x, y, color);
				}
			}
		}

		#endregion

		#region GetTextArea

		public Rectangle GetTextArea(Bitmap bitmap, ColorEquals[] ces)
		{
			return this.GetTextArea(bitmap, Rectangle.Empty, ces);
		}
		public Rectangle GetTextArea(Bitmap bitmap, Rectangle area, ColorEquals[] ces)
		{
			return this.GetTextArea(area, this.GetTextPixels(bitmap, ces));
		}
		public Rectangle GetTextArea(bool[,] pixels)
		{
			return this.GetTextArea(Rectangle.Empty, pixels);
		}
		public Rectangle GetTextArea(Rectangle area, bool[,] pixels)
		{
			int x = 0, y = 0;
			var rec = new Rectangle();
			if (area == Rectangle.Empty)
				area = new Rectangle(0, 0, pixels.GetLength(0), pixels.GetLength(1));
			//Top
			for (y = area.Top; y < area.Bottom; y++)//<=
			{
				for (x = area.Left; x < area.Right; x++)//<=
				{
					if (pixels[x, y])
						break;
				}
				if (x < area.Right)//<=
					break;
			}
			if (y >= area.Bottom)//>
				return Rectangle.Empty;

			rec.Y = y;
			//Left
			for (x = area.Left; x < area.Right; x++)//<=
			{
				for (y = area.Top; y < area.Bottom; y++)//<=
				{
					if (pixels[x, y])
						break;
				}
				if (y < area.Bottom)//<=
					break;
			}
			if (x >= area.Right)//>
				return Rectangle.Empty;

			rec.X = x;
			//Bottom
			for (y = area.Bottom - 1; y >= area.Top; y--)
			{
				for (x = area.Right - 1; x >= area.Left; x--)
				{
					if (pixels[x, y])
						break;
				}
				if (x >= area.Left)
					break;
			}
			if (y < area.Top)
				return Rectangle.Empty;

			rec.Height = y - rec.Y + 1;
			//Right
			for (x = area.Right - 1; x >= area.Left; x--)
			{
				for (y = area.Bottom - 1; y >= area.Top; y--)
				{
					if (pixels[x, y])
						break;
				}
				if (y >= area.Top)
					break;
			}
			if (x < area.Left)
				return Rectangle.Empty;

			rec.Width = x - rec.X + 1;
			//return
			return rec;
		}

		#endregion

		#region Get Text Row Or Column

		public int[] GetTextRow(Bitmap bitmap, Rectangle area, ColorEquals[] ces)
		{
			return this.GetTextRow(area, this.GetTextPixels(bitmap, ces));
		}
		public int[] GetTextRow(Rectangle area, bool[,] pixels)
		{
			int i = area.Bottom, x = 0, y = 0;
			var ls = new List<int>();
			for (y = area.Bottom - 1; y > area.Top; y--)//>=
			{
				x = this.ScanH(pixels, area, y, y - 1);
				if (x == 0 && area.Bottom > y)
				{
					if (y + 1 != i)
						ls.Insert(0, y);
					i = y;
				}
			}
			return ls.ToArray();
		}

		public int[] GetTextColumn(Bitmap bitmap, Rectangle area, ColorEquals[] ces)
		{
			return this.GetTextColumn(area, this.GetTextPixels(bitmap, ces));
		}
		public int[] GetTextColumn(Rectangle area, bool[,] pixels)
		{
			int i = area.Right, x = 0, y = 0;
			var ls = new List<int>();
			for (x = area.Right - 1; x > area.Left; x--)//>=
			{
				y = this.ScanV(pixels, area, x, x - 1);
				if (y == 0 && area.Right > x)
				{
					if (x + 1 != i)
						ls.Insert(0, x);
					i = x;
				}
			}
			return ls.ToArray();
		}

		#endregion

		public Rectangle[] GetTextChar(Bitmap bitmap, ColorEquals[] ces)
		{
			return this.GetTextChar(this.GetTextPixels(bitmap, ces));
		}
		public Rectangle[] GetTextChar(bool[,] pixels)
		{
			var area = this.GetTextArea(pixels);
			var rows = this.GetTextRow(area, pixels);
			if (rows.Length == 0)
				return new Rectangle[] { area };

			int row = 0, col = 0;
			var ls = new List<Rectangle>();
			var rec = new Rectangle(area.X, area.Y, area.Width, rows[row] - area.Y);
			var cols = this.GetTextColumn(rec, pixels);
			if (cols.Length == 0)
			{
				ls.Add(rec);
			}
			else
			{
				col = 0;
				rec = new Rectangle(rec.X, rec.Y, cols[col] - rec.X, rec.Height);
				ls.Add(rec);
				for (col = 1; col < cols.Length; col++)
				{
					rec = new Rectangle(rec.Right, rec.Top, cols[col] - rec.Right, rec.Height);
					ls.Add(rec);
				}
				rec = new Rectangle(rec.Right, rec.Top, area.Right - rec.Right, rec.Height);
				ls.Add(rec);
			}

			for (row = 1; row < rows.Length; row++)
			{
				rec = new Rectangle(area.X, rows[row - 1], area.Width, rows[row] - rows[row - 1]);
				cols = this.GetTextColumn(rec, pixels);
				if (cols.Length == 0)
				{
					ls.Add(rec);
					continue;
				}

				col = 0;
				rec = new Rectangle(rec.X, rec.Y, cols[col] - rec.X, rec.Height);
				ls.Add(rec);
				for (col = 1; col < cols.Length; col++)
				{
					rec = new Rectangle(rec.Right, rec.Top, cols[col] - rec.Right, rec.Height);
					ls.Add(rec);
				}
				rec = new Rectangle(rec.Right, rec.Top, area.Right - rec.Right, rec.Height);
				ls.Add(rec);
			}

			rec = new Rectangle(area.X, rows[row - 1], area.Width, area.Bottom - rows[row - 1]);
			cols = this.GetTextColumn(rec, pixels);
			if (cols.Length == 0)
			{
				ls.Add(rec);
			}
			else
			{
				col = 0;
				rec = new Rectangle(rec.X, rec.Y, cols[col] - rec.X, rec.Height);
				ls.Add(rec);
				for (col = 1; col < cols.Length; col++)
				{
					rec = new Rectangle(rec.Right, rec.Top, cols[col] - rec.Right, rec.Height);
					ls.Add(rec);
				}
				rec = new Rectangle(rec.Right, rec.Top, area.Right - rec.Right, rec.Height);
				ls.Add(rec);
			}

			return ls.ToArray(); ;
		}

		#region GetTextMatch

		public bool[,] GetTextMatch(Bitmap bitmap, ColorEquals[] ces, Rectangle area)
		{
			return this.GetTextMatch(bitmap, ces, area, 0);
		}
		public bool[,] GetTextMatch(Bitmap bitmap, ColorEquals[] ces, Rectangle area, int match)
		{
			return this.GetTextMatch(this.GetTextPixels(bitmap, ces), area, 0);
		}
		public bool[,] GetTextMatch(bool[,] pixels)
		{
			return this.GetTextMatch(pixels, Rectangle.Empty, 0);
		}
		public bool[,] GetTextMatch(bool[,] pixels, int match)
		{
			return this.GetTextMatch(pixels, Rectangle.Empty, match);
		}
		public bool[,] GetTextMatch(bool[,] pixels, Rectangle area)
		{
			return this.GetTextMatch(pixels, area, 0);
		}
		public bool[,] GetTextMatch(bool[,] pixels, Rectangle area, int match)
		{
			Graphics graphics;
			if (pixels == null)
				return null;

			if (area == Rectangle.Empty)
				area = new Rectangle(0, 0, pixels.GetLength(0), pixels.GetLength(1));
			if (area.Width <= 0 || area.Height <= 0)
				return null;

			var rec = this.GetTextArea(area, pixels);
			if (rec.Width <= 0 || rec.Height <= 0)
				return null;

			Bitmap bmp = null;
			if (match > 0)
				bmp = new Bitmap(rec.Width, rec.Height);
			var bs = new bool[rec.Width, rec.Height];
			for (var x = 0; x < rec.Width; x++)
			{
				for (var y = 0; y < rec.Height; y++)
				{
					bs[x, y] = pixels[x + rec.Left, y + rec.Top];
					if (bmp == null)
						continue;

					if (bs[x, y])
						bmp.SetPixel(x, y, Color.White);
					else
						bmp.SetPixel(x, y, Color.Black);
				}
			}
			if (bmp == null)
				return bs;

			var bitmap = bmp;
			for (var i = 0; i < match; i++)
			{
				bmp = new Bitmap(bitmap.Width * 10, bitmap.Height * 10);
				graphics = Graphics.FromImage(bmp);
				graphics.DrawImage(bitmap, 0, 0, bmp.Width, bmp.Height);
				graphics.Dispose();
				for (var x = 0; x < bmp.Width; x++)
				{
					for (var y = 0; y < bmp.Height; y++)
					{
						if (bmp.GetPixel(x, y).ToArgb() == Color.Black.ToArgb())
							bmp.SetPixel(x, y, Color.White);
						else
							bmp.SetPixel(x, y, Color.Black);
					}
				}
				bitmap = bmp;

				bmp = new Bitmap(bitmap.Width / 10, bitmap.Height / 10);
				bs = new bool[bmp.Width, bmp.Height];
				graphics = Graphics.FromImage(bmp);
				graphics.DrawImage(bitmap, 0, 0, bmp.Width, bmp.Height);
				graphics.Dispose();
				for (var x = 0; x < bmp.Width; x++)
				{
					for (var y = 0; y < bmp.Height; y++)
					{
						if (bmp.GetPixel(x, y).ToArgb() == Color.Black.ToArgb())
							bs[x, y] = true;
						else
							bs[x, y] = false;
						if (bs[x, y])
							bmp.SetPixel(x, y, Color.White);
						else
							bmp.SetPixel(x, y, Color.Black);
					}
				}
				bitmap = bmp;
			}
			return bs;
		}

		#endregion

		#region GetTextBitmap

		public Bitmap GetTextBitmap(Bitmap bitmap, ColorEquals[] ces, Rectangle area)
		{
			return this.GetTextBitmap(bitmap, ces, area, 0);
		}
		public Bitmap GetTextBitmap(Bitmap bitmap, ColorEquals[] ces, Rectangle area, int match)
		{
			return this.GetTextBitmap(this.GetTextPixels(bitmap, ces), area, 0);
		}
		public Bitmap GetTextBitmap(bool[,] pixels)
		{
			return this.GetTextBitmap(pixels, Rectangle.Empty, 0);
		}
		public Bitmap GetTextBitmap(bool[,] pixels, int match)
		{
			return this.GetTextBitmap(pixels, Rectangle.Empty, match);
		}
		public Bitmap GetTextBitmap(bool[,] pixels, Rectangle area)
		{
			return this.GetTextBitmap(pixels, area, 0);
		}
		public Bitmap GetTextBitmap(bool[,] pixels, Rectangle area, int match)
		{
			const int I = 16;
			Graphics graphics;
			if (pixels == null)
				return null;

			if (area == Rectangle.Empty)
				area = new Rectangle(0, 0, pixels.GetLength(0), pixels.GetLength(1));
			if (area.Width <= 0 || area.Height <= 0)
				return null;

			var rec = this.GetTextArea(area, pixels);
			if (rec.Width <= 0 || rec.Height <= 0)
				return null;

			Bitmap bmp = new Bitmap(rec.Width * I + 1, rec.Height * I + 1);
			for (var x = 0; x < bmp.Width; x++)
			{
				for (var y = 0; y < bmp.Height; y++)
				{
					if (x % I == 0 || y % I == 0)
						bmp.SetPixel(x, y, Color.Gray);
					else if (pixels[x / I + rec.Left, y / I + rec.Top])
						bmp.SetPixel(x, y, Color.White);
					else
						bmp.SetPixel(x, y, Color.Black);
				}
			}
			var bitmap = bmp;

			for (var i = 0; i < match; i++)
			{
				bmp = new Bitmap(bitmap.Width * 10, bitmap.Height * 10);
				graphics = Graphics.FromImage(bmp);
				graphics.DrawImage(bitmap, 0, 0, bmp.Width, bmp.Height);
				graphics.Dispose();
				for (var x = 0; x < bmp.Width; x++)
				{
					for (var y = 0; y < bmp.Height; y++)
					{
						if (bmp.GetPixel(x, y).ToArgb() == Color.Black.ToArgb())
							bmp.SetPixel(x, y, Color.White);
						else
							bmp.SetPixel(x, y, Color.Black);
					}
				}
				bitmap = bmp;

				bmp = new Bitmap(bitmap.Width / 10, bitmap.Height / 10);
				graphics = Graphics.FromImage(bmp);
				graphics.DrawImage(bitmap, 0, 0, bmp.Width, bmp.Height);
				graphics.Dispose();
				for (var x = 0; x < bmp.Width; x++)
				{
					for (var y = 0; y < bmp.Height; y++)
					{
						if (bmp.GetPixel(x, y).ToArgb() == Color.Black.ToArgb())
							bmp.SetPixel(x, y, Color.White);
						else
							bmp.SetPixel(x, y, Color.Black);
					}
				}
				bitmap = bmp;
			}
			return bitmap;
		}

		#endregion

		private int ScanH(bool[,] pixels, Rectangle rectangle, int h, int nextH)
		{
			if (pixels == null)
				return 0;

			if (rectangle == Rectangle.Empty)
				rectangle = new Rectangle(0, 0, pixels.GetLength(0), pixels.GetLength(1));
			if (h < rectangle.Top || h >= rectangle.Bottom)//>
				return 0;

			var count = 0;
			for (var i = rectangle.Left; i < rectangle.Right; i++)
			{
				if (pixels[i, h])
					count++;
			}
			return count;
		}

		private int ScanV(bool[,] pixels, Rectangle rectangle, int v, int nextV)
		{
			if (pixels == null)
				return 0;

			if (rectangle == Rectangle.Empty)
				rectangle = new Rectangle(0, 0, pixels.GetLength(0), pixels.GetLength(1));
			if (v < rectangle.Left || v >= rectangle.Right)//>
				return 0;

			var count = 0;
			for (var i = rectangle.Top; i < rectangle.Bottom; i++)
			{
				if (pixels[v, i])
					count++;
			}
			return count;
		}

		#region ScanAll String

		public string ScanAll(Bitmap bitmap, ColorEquals[] ces, MatchModelFileDictionary matchModelFileDictionary)
		{
			return this.ScanAll(bitmap, ces, matchModelFileDictionary, Rectangle.Empty);
		}
		public string ScanAll(Bitmap bitmap, ColorEquals[] ces, MatchModelFileDictionary matchModelFileDictionary, Rectangle area)
		{
			if (bitmap == null || ces == null || ces.Length == 0)
				return null;

			var pixels = this.GetTextPixels(bitmap, ces, area);
			if (pixels == null)
				return null;

			return this.ScanAll(pixels, matchModelFileDictionary);
		}
		public string ScanAll(bool[,] pixels, MatchModelFileDictionary matchModelFileDictionary)
		{
			Rectangle rec;
			var sb = new StringBuilder();
			var e = new OcrEventArgs();
			var area = this.GetTextArea(pixels);
			int i = 0, x = 0, x0 = 0, x1 = 0, y = 0, y0 = 0, y1 = 0;
			for (y = area.Top; y <= area.Bottom; y++)//y < area.Bottom
			{
				if (y == area.Bottom)
					i = 0;
				else
					i = this.ScanH(pixels, area, y, y + 1);
				if (i != 0)
				{
					y0 = 0;
					y1++;
					continue;
				}

				y0++;
				e.XLines = 0;
				e.YLines = y0;
				e.Result = null;
				this.OnMatchText(e);
				if (!string.IsNullOrEmpty(e.Result))
					sb.Append(e.Result);
				if (y1 > 0)
				{
					e.XLines = 0;
					e.YLines = 0;
					e.Result = Environment.NewLine;
					this.OnMatchText(e);
					if (!string.IsNullOrEmpty(e.Result))
						sb.Append(e.Result);
					x0 = x1 = 0;
					rec = new Rectangle(0, y - y1, area.Width, y1);
					for (x = area.Left; x <= area.Right; x++)//x < area.Right
					{
						if (x == area.Right)
							i = 0;
						else
							i = this.ScanV(pixels, rec, x, x + 1);
						if (i != 0)
						{
							x0 = 0;
							x1++;
							continue;
						}

						x0++;
						e.XLines = x0;
						e.YLines = 0;
						e.Result = null;
						this.OnMatchText(e);
						if (!string.IsNullOrEmpty(e.Result))
							sb.Append(e.Result);
						if (x1 > 0)
							sb.Append(this.Scan(this.GetTextTrim(new Rectangle(x - x1, rec.Y, x1, rec.Height), pixels), matchModelFileDictionary));
						x1 = 0;
					}
				}
				y1 = 0;
			}
			return sb.ToString();
		}

		public string Scan(bool[,] pixels, MatchModelFileDictionary matchModelFileDictionary)
		{
			return this.Scan(pixels, matchModelFileDictionary, MatchModelLevel.Highest, 0);
		}
		public string Scan(bool[,] pixels, MatchModelFileDictionary matchModelFileDictionary, int precision)
		{
			return this.Scan(pixels, matchModelFileDictionary, MatchModelLevel.Highest, precision);
		}
		public string Scan(bool[,] pixels, MatchModelFileDictionary matchModelFileDictionary, MatchModelLevel matchModelLevel)
		{
			return this.Scan(pixels, matchModelFileDictionary, matchModelLevel, 0);
		}
		public string Scan(bool[,] pixels, MatchModelFileDictionary matchModelFileDictionary, MatchModelLevel matchModelLevel, int precision)
		{
			var e = new OcrEventArgs();
			if (pixels == null || matchModelFileDictionary == null || matchModelFileDictionary.Count == 0)
			{
				this.OnMatchText(e);
				return e.Result;
			}

			var xx = pixels.GetLength(0);
			var yy = pixels.GetLength(1);
			var rec = new Rectangle(0, 0, xx, yy);
			pixels = COcr.Value.GetTextTrim(rec, pixels);
			if (pixels == null)
			{
				this.OnMatchText(e);
				return e.Result;
			}

			e.MatchLevel = MatchModelLevel.Lowest;
			e.Source = new MatchModel();
			e.Source.TextPixels = pixels;
			e.Source.TextBuffer = MatchModel.GetBuffer(e.Source.TextPixels);
			e.Source.TextAscii = MatchModel.GetAscii(e.Source.TextBuffer);
			e.Source.TextB64 = MatchModel.GetB64(e.Source.TextBuffer);
			foreach (var mmd in matchModelFileDictionary.Values)
			{
				if (mmd.ContainsKey(e.Source.TextB64))
				{
					if (int.MaxValue > mmd[e.Source.TextB64].Frequency)
						mmd[e.Source.TextB64].Frequency++;
					e.Result = mmd[e.Source.TextB64].Text;
					e.Matchs = new MatchModel[] { mmd[e.Source.TextB64] };
					this.OnMatchText(e);
					return e.Result;
				}

				if (mmd.ContainsKey(e.Source.TextAscii))
				{
					if (int.MaxValue > mmd[e.Source.TextAscii].Frequency)
						mmd[e.Source.TextAscii].Frequency++;
					e.Result = mmd[e.Source.TextAscii].Text;
					e.Matchs = new MatchModel[] { mmd[e.Source.TextAscii] };
					this.OnMatchText(e);
					return e.Result;
				}
			}
			if (matchModelLevel == MatchModelLevel.Lowest)//MatchModelLevel.Lowest
			{
				e.Result = " ";//null
				e.Matchs = null;
				this.OnMatchText(e);
				return e.Result;
			}

			int i;
			int mx, my;
			if (precision == 0)
				precision = 80;//100;//
			else
				precision = Math.Abs(precision) % 100;
			var t = string.Empty;
			e.MatchLevel = MatchModelLevel.Normal;
			foreach (var mmd in matchModelFileDictionary.Values)
			{
				foreach (var mm in mmd.Values)
				{
					mm.Matched = 0;
					mm.MatchTotal = 0;
					mx = mm.TextPixels.GetLength(0);
					my = mm.TextPixels.GetLength(1);
					for (var x = 0; x < mx; x++)
					{
						for (var y = 0; y < my; y++)
						{
							if (x >= xx || y >= yy || mm.TextPixels[x, y] ^ pixels[x, y])
								mm.Matched++;
							mm.MatchTotal++;
						}
					}
					if (mm.Matched * 100 / mm.MatchTotal > 100 - precision)
						continue;

					t += mm.Text;
					for (var x = 0; x < mx; x++)
					{
						for (var y = 0; y < my; y++)
						{
							if (x >= xx || y >= yy || mm.TextPixels[x, y] ^ pixels[x, y])
								mm.Matched--;
							else
								pixels[x, y] = false;
							mm.MatchTotal--;
						}
					}
					rec = new Rectangle(0, 0, xx, yy);
					pixels = COcr.Value.GetTextTrim(rec, pixels);
					if (pixels == null)
					{
						if (string.IsNullOrEmpty(t))
							e.Result = " ";//null
						else
							e.Result = t;
						e.Matchs = null;
						this.OnMatchText(e);
						return e.Result;
					}

					xx = pixels.GetLength(0);
					yy = pixels.GetLength(1);
					//return mm.Text;
				}
			}
			if (!string.IsNullOrEmpty(t) || matchModelLevel != MatchModelLevel.Highest)//MatchModelLevel.Normal
			{
				if (string.IsNullOrEmpty(t))
					e.Result = " ";//null
				else
					e.Result = t;
				e.Matchs = null;
				this.OnMatchText(e);
				return e.Result;
			}

			e.MatchLevel = MatchModelLevel.Highest;
			foreach (var mmd in matchModelFileDictionary.Values)
			{
				foreach (var key in mmd.Keys)
				{
					i = e.Source.TextB64.IndexOf(key);
					if (i >= 0)
					{
						if (int.MaxValue > mmd[key].Frequency)
							mmd[key].Frequency++;
						t += mmd[key].Text;
						e.Source.TextB64 = e.Source.TextB64.Substring(0, i) + e.Source.TextB64.Substring(i + key.Length);
					}

					i = e.Source.TextAscii.IndexOf(key);
					if (i >= 0)
					{
						if (int.MaxValue > mmd[key].Frequency)
							mmd[key].Frequency++;
						t += mmd[key].Text;
						e.Source.TextAscii = e.Source.TextAscii.Substring(0, i) + e.Source.TextAscii.Substring(i + key.Length);
					}
				}
			}
			if (string.IsNullOrEmpty(t))
				e.Result = " ";//null
			else
				e.Result = t;
			e.Matchs = null;
			this.OnMatchText(e);
			return e.Result;
		}

		#endregion

		#region ScanAll Image

		public Rectangle[] ScanAll(string big, string small)
		{
			var model = ImageModel.GetModel(small);
			if (model == null)
				model = new ImageModel(small);

			return this.ScanAll(new Bitmap(big), model, Rectangle.Empty);
		}
		public Rectangle[] ScanAll(Bitmap big, string small)
		{
			var model = ImageModel.GetModel(small);
			if (model == null)
				model = new ImageModel(small);

			return this.ScanAll(big, model, Rectangle.Empty);
		}
		public Rectangle[] ScanAll(string big, string small, Rectangle area)
		{
			var model = ImageModel.GetModel(small);
			if (model == null)
				model = new ImageModel(small);

			return this.ScanAll(new Bitmap(big), model, area);
		}
		public Rectangle[] ScanAll(Bitmap big, string small, Rectangle area)
		{
			var model = ImageModel.GetModel(small);
			if (model == null)
				model = new ImageModel(small);

			return this.ScanAll(big, model, area);
		}
		public Rectangle[] ScanAll(Image big, Image small)
		{
			var bmp = small as Bitmap;
			if (bmp == null)
				return this.ScanAll(new Bitmap(big), new Bitmap(small), Rectangle.Empty);

			return this.ScanAll(new Bitmap(big), bmp, Rectangle.Empty);
		}
		public Rectangle[] ScanAll(Bitmap big, Bitmap small)
		{
			var model = ImageModel.GetModel(small);
			if (model == null)
				model = new ImageModel(small);

			return this.ScanAll(big, model, Rectangle.Empty);
		}
		public Rectangle[] ScanAll(Image big, Image small, Rectangle area)
		{
			var bmp = small as Bitmap;
			if (bmp == null)
				return this.ScanAll(new Bitmap(big), new Bitmap(small), area);

			return this.ScanAll(new Bitmap(big), bmp, area);
		}
		public Rectangle[] ScanAll(Bitmap big, Bitmap small, Rectangle area)
		{
			var model = ImageModel.GetModel(small);
			if (model == null)
				model = new ImageModel(small);

			return this.ScanAll(big, model, area);
		}
		public Rectangle[] ScanAll(Image big, ImageModel model)
		{
			return this.ScanAll(new Bitmap(big), model, Rectangle.Empty);
		}
		public Rectangle[] ScanAll(Bitmap big, ImageModel model)
		{
			return this.ScanAll(big, model, Rectangle.Empty);
		}
		public Rectangle[] ScanAll(Image big, ImageModel model, Rectangle area)
		{
			return this.ScanAll(new Bitmap(big), model, area);
		}
		public Rectangle[] ScanAll(Bitmap big, ImageModel model, Rectangle area)
		{
			//(byte)(.299 * red + .587 * green + .114 * blue);//RGB -> GDI+:BGR
			//big = COcr.GetBitmap(big);
			int x = 0, y = 0;
			if (area == Rectangle.Empty)
				area = new Rectangle(0, 0, big.Width, big.Height);
			var result = new Rectangle(area.Left, area.Top, 0, 0);//Width == 0 && Height == 0
			var results = new List<Rectangle>();
			var recR = new Rectangle(0, 0, big.Width, big.Height);
			var recM = new Rectangle(0, 0, model.Model.Width, model.Model.Height);
			var bdb = big.LockBits(recR, ImageLockMode.ReadOnly, model.ModelFormat);
			var bds = model.LockBits();
			var iSH = model.Model.Height;
			var iSW = model.Model.Width;
			var iS = bds.Stride;
			var iW = iS / bds.Width;
			for (y = 0; y < model.Length; y++)
			{
				recR = new Rectangle(area.X, result.Bottom, 0, 0);
				for (x = 0; x < model[y].Length; x++)
				{
					var ma = model[y, x];
					if (ma.Relatively == 0 || ma.Relatively > results.Count)
					{
						recM = ma.Area;
						if (result.Width == 0 && result.Height == 0)//开始
						{
							if ((recM.Y > byte.MaxValue || recM.Y > iSH / 2) && (recM.X > byte.MaxValue || recM.X > iSW / 2))//自动绝对位置:X,Y
								recR = new Rectangle(recM.X, recM.Y, recM.Width, recM.Height);
							else if (recM.Y > byte.MaxValue || recM.Y > iSH / 2)//自动绝对位置:Y
								recR = new Rectangle(area.X, recM.Y, recM.Width, recM.Height);
							else if (recM.X > byte.MaxValue || recM.X > iSW / 2)//自动绝对位置:X
								recR = new Rectangle(recM.X, area.Y, recM.Width, recM.Height);
							else//全局位置
								recR = new Rectangle(area.X, area.Y, recM.Width, recM.Height);
						}
						else
						{
							if (x > 0)//自动绝对位置:Y
								recR = new Rectangle(recR.Right, recR.Y, recM.Width, recM.Height);
							else if (recM.X > byte.MaxValue || recM.X > iSW / 2)//自动绝对位置:X
								recR = new Rectangle(recM.X, recR.Bottom, recM.Width, recM.Height);
							else//全局位置:Y
								recR = new Rectangle(area.X, recR.Bottom, recM.Width, recM.Height);
						}
					}
					else if (ma.Relatively > 0)
					{
						recR = Rectangle.Empty;
						recM = Rectangle.Empty;
						for (var i = ma.Relatively - 1; i >= 0; i--)
						{
							recR = results[i];
							recM = model.GetItem(i).Area;
							if (recR != Rectangle.Empty)
								break;
						}
						if (recR == Rectangle.Empty || recM == Rectangle.Empty)
						{
							recR.X = area.X;
							//recR.Y = recR.Bottom;
						}
						recR.X = recR.X + ma.X - recM.X;
						recR.Y = recR.Y + ma.Y - recM.Y;
						recM = ma.Area;
						recR.Width = recM.Width;
						recR.Height = recM.Height;
					}
					else
					{
						recM = ma.Area;
						recR = new Rectangle(area.X, area.Y, recM.Width, recM.Height);
					}
					if (ma.AreaLimit == Rectangle.Empty)
					{
						recR.Width = area.Right - recR.X;
						recR.Height = area.Bottom - recR.Y;
					}
					else
					{
						if (ma.AreaLimit.X != 0)
						{
							recR.X += ma.AreaLimit.X;
							if (ma.AreaLimit.X < 0)
								recR.Width -= ma.AreaLimit.X;
						}
						if (ma.AreaLimit.Y != 0)
						{
							recR.Y += ma.AreaLimit.Y;
							if (ma.AreaLimit.Y < 0)
								recR.Height -= ma.AreaLimit.Y;
						}
						recR.Width += ma.AreaLimit.Width;
						recR.Height += ma.AreaLimit.Height;
					}
					if (recR.X < area.X)
					{
						recR.Width = recR.Width - area.X + recR.X;
						recR.X = area.X;
					}
					if (recR.Y < area.Y)
					{
						recR.Height = recR.Height - area.Y + recR.Y;
						recR.Y = area.Y;
					}
					if (recR.Right > area.Right)
					{
						recR.Width = recR.Width - recR.Right + area.Right;
					}
					if (recR.Bottom > area.Bottom)
					{
						recR.Height = recR.Height - recR.Bottom + area.Bottom;
					}
					if (recM.Width > recR.Width || recM.Height > recR.Height)
						recR = Rectangle.Empty;
					else
						recR = this.Scan(bdb, recR, ma);
					results.Add(recR);
					if (recR == Rectangle.Empty)
					{
						if (ma.ContinueOnUnmatch)
							continue;

						big.UnlockBits(bdb);
						results.Insert(0, Rectangle.Empty);
						return results.ToArray();
					}

					if (result.Width == 0 && result.Height == 0)
					{
						result = recR;
						continue;
					}

					if (result.X > recR.X)
					{
						result.Width = result.Width + result.X - recR.X;
						result.X = recR.X;
					}
					if (result.Top > recR.Top)
					{
						result.Height = result.Height + result.Y - recR.Y;
						result.Y = recR.Y;
					}
					if (recR.Right > result.Right)
						result.Width = result.Width + recR.Right - result.Right;
					if (recR.Bottom > result.Bottom)
						result.Height = result.Height + recR.Bottom - result.Bottom;
				}
			}
			big.UnlockBits(bdb);
			results.Insert(0, result);
			return results.ToArray();
		}
		private Rectangle Scan(BitmapData bigImage, Rectangle bigArea, ImageModelArea smallArea)
		{
			var full = smallArea.ImageModel.IsFull;
			var smallImage = smallArea.ImageModel.LockBits();
			if (bigArea == Rectangle.Empty)
				bigArea = new Rectangle(0, 0, bigImage.Width, bigImage.Height);
			if (smallArea.Area == Rectangle.Empty)
				smallArea.Area = new Rectangle(0, 0, smallImage.Width, smallImage.Height);
			var precision = smallArea.Precision;
			if (precision > 0)
				precision %= 100;
			else
				precision = 80;
			if (precision == 0)
				precision = 100;
			var tolerance = smallArea.Tolerance;
			if (tolerance == byte.MaxValue)
				tolerance = 32;
			int i, x, y;
			int iB = 0, iS = 0, iM = 0, iT = 0, iA = 0;
			var iSB = bigImage.Stride;
			var iHB = bigArea.Bottom - smallArea.Height;// bdb.Height - bds.Height;
			var iWB = bigArea.Right - smallArea.Width;// bdb.Width - bds.Width;
			var iSS = smallImage.Stride;
			var iTS = smallArea.Top;
			var iLS = smallArea.Left;
			var iHS = smallArea.Height;
			var iWS = smallArea.Width;
			var iW = iSS / smallImage.Width;
			var iXBStart = bigArea.X;
			var iYBStart = bigArea.Y;
			unsafe
			{
				var bb = (byte*)bigImage.Scan0.ToPointer();
				var bs = (byte*)smallImage.Scan0.ToPointer();
				for (var iYB = iYBStart; iYB <= iHB; iYB++)
				{
					for (var iXB = iXBStart; iXB <= iWB; iXB++)
					{
						iT = iM = 0;
						for (y = 0; y < iHS; y++)//small:Y
						{
							for (x = 0; x < iWS; x++)//small:X
							{
								iS = (y + iTS) * iSS + (x + iLS) * iW;

								//空白（忽略）比较
								for (i = 0; i < iW && !full; i++)
								{
									if (bs[iS + i] != bs[i])
										break;
								}
								if (i >= iW)
									continue;

								//实际像素比较
								iB = (iYB + y) * iSB + (iXB + x) * iW;
								for (i = 0; i < iW; i++)
								{
									//if (bb[iB + i] != bs[iS + i])
									//	break;
									if (bb[iB + i] == bs[iS + i])
										continue;

									if (bb[iB + i] - bs[iS + i] > tolerance || bs[iS + i] - bb[iB + i] > tolerance)
										break;
								}
								if (i >= iW)
									iM++;
								iT++;
								if (iA > 0 && (iT - iM) * 100 / iA > 100 - precision)
									break;
							}
							if (x < iWS)
								break;
						}
						if (iA == 0)
							iA = iT;
						if (iM == 0 || iT == 0 || precision > iM * 100 / iT)
							continue;

						return new Rectangle(iXB, iYB, smallArea.Width, smallArea.Height);
					}
				}
			}
			return Rectangle.Empty;
		}

		#endregion

		public static Bitmap GetBitmap(string path)
		{
			return COcr.GetBitmap(new Bitmap(path));
		}
		public static Bitmap GetBitmap(Image image)
		{
			var bmp = image as Bitmap;
			if (bmp == null)
				bmp = new Bitmap(image);
			if (bmp.RawFormat.Equals(ImageFormat.Bmp))
				return bmp;

			var ms = new MemoryStream();
			bmp.Save(ms, ImageFormat.Bmp);
			bmp.Dispose();
			bmp = new Bitmap(ms);
			ms.Close();
			ms.Dispose();
			return bmp;
		}

		public static int OpenMatch(MatchModelFileDictionary matchModelFileDictionary, FileInfo file)
		{
			if (matchModelFileDictionary == null || file == null)
				return -1;

			if (!matchModelFileDictionary.ContainsKey(file))
				matchModelFileDictionary.Add(file, new MatchModelDictionary());
			if (!file.Exists)
				return 0;

			var mms = BaseCommon.OpenObject<MatchModel[]>(file);
			if (mms == null || mms.Length == 0)
				return 0;

			for (var i = 0; i < mms.Length; i++)
			{
				mms[i].TextBuffer = MatchModel.GetBuffer(mms[i].TextPixels);
				mms[i].TextAscii = MatchModel.GetAscii(mms[i].TextBuffer);
				mms[i].TextB64 = MatchModel.GetB64(mms[i].TextBuffer);
				if (!matchModelFileDictionary[file].ContainsKey(mms[i].TextB64))
					matchModelFileDictionary[file].Add(mms[i].TextB64, mms[i]);
			}

			return mms.Length;
		}

		public static int SaveMatch(MatchModelFileDictionary matchModelFileDictionary)
		{
			if (matchModelFileDictionary == null || matchModelFileDictionary.Count == 0)
				return 0;

			MatchModel[] mms;
			foreach (var file in matchModelFileDictionary.Keys)
			{
				mms = new MatchModel[matchModelFileDictionary[file].Values.Count];
				matchModelFileDictionary[file].Values.CopyTo(mms, 0);
				BaseCommon.SaveObject(mms, file);
			}
			return matchModelFileDictionary.Count;
		}

		public static Image CaptureWindow(IntPtr handle)
		{
			var hdcSrc = User32.GetWindowDC(handle);
			var rect = new RECT();
			User32.GetWindowRect(handle, out rect);
			var hdcDest = Gdi32.CreateCompatibleDC(hdcSrc);
			var hBitmap = Gdi32.CreateCompatibleBitmap(hdcSrc, rect.Width, rect.Height);
			var hOld = Gdi32.SelectObject(hdcDest, hBitmap);
			Gdi32.BitBlt(hdcDest, 0, 0, rect.Width, rect.Height, hdcSrc, 0, 0, Gdi32.SRCCOPY_BITBLT_DWROP);
			Gdi32.SelectObject(hdcDest, hOld);
			Gdi32.DeleteDC(hdcDest);
			User32.ReleaseDC(handle, hdcSrc);
			var img = Image.FromHbitmap(hBitmap);
			Gdi32.DeleteObject(hBitmap);
			return img;
		}

		public static Bitmap Xor(Bitmap x, Bitmap y)
		{
			if (x.Width != y.Width || x.Height != y.Height || x.PixelFormat != y.PixelFormat)
				return null;

			var bmp = new Bitmap(x.Width, x.Height, x.PixelFormat);
			bmp = new Bitmap(bmp.Width, bmp.Height, bmp.PixelFormat);
			var bt = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat);
			var bt0 = x.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat);
			var bt1 = y.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat);
			unsafe
			{
				var bs = (byte*)bt.Scan0.ToPointer();
				var bs0 = (byte*)bt0.Scan0.ToPointer();
				var bs1 = (byte*)bt1.Scan0.ToPointer();
				for (var h = 0; h < bt.Height; h++)
				{
					for (var w = 0; w < bt.Stride; w++)
					{
						var p = h * bt.Stride + w;
						bs[p] = (byte)(bs0[p] ^ bs1[p]);
					}
				}
			}
			bmp.UnlockBits(bt);
			x.UnlockBits(bt0);
			y.UnlockBits(bt1);
			return bmp;
		}

		public static Bitmap ConvertGray(Bitmap bitmap)
		{
			return COcr.ConvertGray(bitmap, GrayFlag.Weighting);
		}
		public static Bitmap ConvertGray(Bitmap bitmap, GrayFlag flag)
		{
			if (bitmap == null)
				return null;

			if (bitmap.PixelFormat == PixelFormat.Format32bppRgb)
				bitmap = new Bitmap(bitmap);
			var p = 0;
			var a = byte.MinValue;
			var r = byte.MinValue;
			var g = byte.MinValue;
			var b = byte.MinValue;
			var w = bitmap.Width;
			var h = bitmap.Height;
			var f = bitmap.PixelFormat;
			var bmp = new Bitmap(1, 1, PixelFormat.Format8bppIndexed);
			var palette = bmp.Palette;
			bmp.Dispose();
			bmp = new Bitmap(w, h, PixelFormat.Format8bppIndexed);
			var bdt = bmp.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.WriteOnly, bmp.PixelFormat);
			var bitmapData = bitmap.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadOnly, f);
			var bdtS = Math.Abs(bdt.Stride);
			var bitmapDataS = Math.Abs(bitmapData.Stride);
			var bdtW = bdtS / w;
			var bitmapDataW = bitmapDataS / w;
			for (var i = 0; i < palette.Entries.Length; i++)
				palette.Entries[i] = Color.FromArgb(i, i, i);
			bmp.Palette = palette;
			unsafe
			{
				var bdtP = (byte*)bdt.Scan0.ToPointer();
				var bitmapDataP = (byte*)bitmapData.Scan0.ToPointer();
				for (var y = 0; y < h; y++)
				{
					for (var x = 0; x < w; x++)
					{
						switch (f)//RGB -> GDI+:BGR
						{
							case PixelFormat.Format24bppRgb:
								p = y * bitmapDataS + x * bitmapDataW;
								b = bitmapDataP[p];
								g = bitmapDataP[p + 1];
								r = bitmapDataP[p + 2];
								a = byte.MaxValue;//byte.MinValue;
								p = y * bdtS + x * bdtW;
								if ((flag & GrayFlag.Max) == GrayFlag.Max)
								{
									if (r > g)
										g = r;
									if (g > b)
										b = g;
									bdtP[p] = b;
								}
								else if ((flag & GrayFlag.Min) == GrayFlag.Min)
								{
									if (r > g)
										r = g;
									if (r > b)
										r = b;
									bdtP[p] = r;
								}
								else
								{
									if ((flag & GrayFlag.A) == GrayFlag.A)
										bdtP[p] = a;
									else if ((flag & GrayFlag.R) == GrayFlag.R)
										bdtP[p] = r;
									else if ((flag & GrayFlag.G) == GrayFlag.G)
										bdtP[p] = g;
									else if ((flag & GrayFlag.B) == GrayFlag.B)
										bdtP[p] = b;
									else if ((flag & GrayFlag.Average) == GrayFlag.Average)
										bdtP[p] = (byte)((r + g + b) / 3);
									else// GrayFlag.Weighting:
										bdtP[p] = (byte)(.299 * r + .587 * g + .114 * b);
								}
								break;
							case PixelFormat.Format32bppArgb:
								p = y * bitmapDataS + x * bitmapDataW;
								b = bitmapDataP[p];
								g = bitmapDataP[p + 1];
								r = bitmapDataP[p + 2];
								a = bitmapDataP[p + 3];
								p = y * bdtS + x * bdtW;
								if ((flag & GrayFlag.Max) == GrayFlag.Max)
								{
									if (r > g)
										g = r;
									if (g > b)
										b = g;
									bdtP[p] = b;
								}
								else if ((flag & GrayFlag.Min) == GrayFlag.Min)
								{
									if (r > g)
										r = g;
									if (r > b)
										r = b;
									bdtP[p] = r;
								}
								else
								{
									if ((flag & GrayFlag.A) == GrayFlag.A)
										bdtP[p] = a;
									else if ((flag & GrayFlag.R) == GrayFlag.R)
										bdtP[p] = r;
									else if ((flag & GrayFlag.G) == GrayFlag.G)
										bdtP[p] = g;
									else if ((flag & GrayFlag.B) == GrayFlag.B)
										bdtP[p] = b;
									else if ((flag & GrayFlag.Average) == GrayFlag.Average)
										bdtP[p] = (byte)((r + g + b) / 3);
									else// GrayFlag.Weighting:
										bdtP[p] = (byte)(.299 * r + .587 * g + .114 * b);
								}
								break;
							default:
								throw new ArgumentOutOfRangeException(MethodBase.GetCurrentMethod().GetParameters()[0].Name, f, "无法处理的图像格式！");
						}
					}
				}
			}
			bmp.UnlockBits(bdt);
			bitmap.UnlockBits(bitmapData);
			return bmp;
		}

		#region FilterColor

		public static Bitmap FilterColor(Bitmap bitmap, byte a, byte r, byte g, byte b)
		{
			return COcr.FilterColor(bitmap, new ColorEquals[] { new ColorEquals(a, r, g, b) });
		}
		public static Bitmap FilterColor(Bitmap bitmap, byte la, byte lr, byte lg, byte lb, byte ra, byte rr, byte rg, byte rb)
		{
			return COcr.FilterColor(bitmap, new ColorEquals[] { new ColorEquals(la, lr, lg, lb, ra, rr, rg, rb) });
		}
		public static Bitmap FilterColor(Bitmap bitmap, Color left, Color right)
		{
			return COcr.FilterColor(bitmap, new ColorEquals[] { new ColorEquals(left, right) });
		}
		public static Bitmap FilterColor(Bitmap bitmap, ColorEquals colorEquals)
		{
			return COcr.FilterColor(bitmap, new ColorEquals[] { colorEquals });
		}
		public static Bitmap FilterColor(Bitmap bitmap, ColorEquals colorEquals0, ColorEquals colorEquals1)
		{
			return COcr.FilterColor(bitmap, new ColorEquals[] { colorEquals0, colorEquals1 });
		}
		public static Bitmap FilterColor(Bitmap bitmap, ColorEquals colorEquals0, ColorEquals colorEquals1, ColorEquals colorEquals2)
		{
			return COcr.FilterColor(bitmap, new ColorEquals[] { colorEquals0, colorEquals1, colorEquals2 });
		}
		public static Bitmap FilterColor(Bitmap bitmap, ColorEquals[] colorEquals)
		{
			var i = 0;
			var p = 0;
			var a = byte.MinValue;
			var r = byte.MinValue;
			var g = byte.MinValue;
			var b = byte.MinValue;
			var w = bitmap.Width;
			var h = bitmap.Height;
			var f = bitmap.PixelFormat;
			var bdt = bitmap.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadWrite, f);
			var bdtS = bdt.Stride;
			var bdtW = bdtS / w;
			unsafe
			{
				var bdtP = (byte*)bdt.Scan0.ToPointer();
				for (var y = 0; y < h; y++)
				{
					for (var x = 0; x < w; x++)
					{
						p = y * bdtS + x * bdtW;
						switch (f)//RGB -> GDI+:BGR
						{
							case PixelFormat.Format8bppIndexed:
								b = byte.MaxValue;//byte.MinValue;
								g = byte.MaxValue;//byte.MinValue;
								r = byte.MaxValue;//byte.MinValue;
								a = bdtP[p];
								break;
							case PixelFormat.Format24bppRgb:
								b = bdtP[p];
								g = bdtP[p + 1];
								r = bdtP[p + 2];
								a = byte.MaxValue;//byte.MinValue;
								break;
							case PixelFormat.Format32bppRgb:
								b = bdtP[p];
								g = bdtP[p + 1];
								r = bdtP[p + 2];
								a = byte.MaxValue;//byte.MinValue;
								break;
							case PixelFormat.Format32bppArgb:
								b = bdtP[p];
								g = bdtP[p + 1];
								r = bdtP[p + 2];
								a = bdtP[p + 3];
								break;
						}
						for (i = 0; i < colorEquals.Length; i++)
						{
							if (a >= colorEquals[i]._LA && colorEquals[i]._RA >= a &&
								r >= colorEquals[i]._LR && colorEquals[i]._RR >= r &&
								g >= colorEquals[i]._LG && colorEquals[i]._RG >= g &&
								b >= colorEquals[i]._LB && colorEquals[i]._RB >= b)
								break;
						}
						if (colorEquals.Length > i)
							continue;

						for (i = 0; i < bdtW; i++)
							bdtP[p + i] = byte.MinValue;
					}
				}
			}
			bitmap.UnlockBits(bdt);
			return bitmap;
		}

		#endregion

		/// <summary>
		/// 边界模糊
		/// </summary>
		/// <param name="bitmap">要处理的图片</param>
		/// <returns>模糊后图片</returns>
		public static Bitmap EdgeBlur(Bitmap bitmap)
		{
			var pM = 0;
			var pT = 0;
			var pL = 0;
			var pB = 0;
			var pR = 0;
			var v = 0;
			var c = 0;

			var top = byte.MinValue;
			var bottom = byte.MinValue;
			var left = byte.MinValue;
			var right = byte.MinValue;
			var value = byte.MinValue;

			var width = bitmap.Width;
			var height = bitmap.Height;
			var bdt = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, bitmap.PixelFormat);
			//var formatSize = Image.GetPixelFormatSize(bitmap.PixelFormat);
			var stride = bdt.Stride;
			var formatSize = stride / width;
			var bs = new byte[height * stride];
			Marshal.Copy(bdt.Scan0, bs, 0, bs.Length);
			unsafe
			{
				var bdp = (byte*)bdt.Scan0.ToPointer();
				for (var y = 0; y < height; y++)
				{
					for (var x = 0; x < width; x++)
					{
						for (var i = 0; i < formatSize; i++)
						{
							pM = y * stride + x * formatSize + i;
							pT = (y - 1) * stride + x * formatSize + i;
							pL = y * stride + (x - 1) * formatSize + i;
							pB = (y + 1) * stride + x * formatSize + i;
							pR = y * stride + (x + 1) * formatSize + i;
							if (y <= 0)
							{
								if (x <= 0)
								{
									top = byte.MinValue;
									left = byte.MinValue;
									bottom = bs[pB];
									right = bs[pR];
								}
								else if (x >= width - 1)
								{
									top = byte.MinValue;
									left = bs[pL];
									bottom = bs[pB];
									right = byte.MinValue;
								}
								else
								{
									top = byte.MinValue;
									left = bs[pL];
									bottom = bs[pB];
									right = bs[pR];
								}
							}
							else if (y >= height - 1)
							{
								if (x <= 0)
								{
									top = bs[pT];
									left = byte.MinValue;
									bottom = byte.MinValue;
									right = bs[pR];
								}
								else if (x >= width - 1)
								{
									top = bs[pT];
									left = bs[pL];
									bottom = byte.MinValue;
									right = byte.MinValue;
								}
								else
								{
									top = bs[pT];
									left = bs[pL];
									bottom = byte.MinValue;
									right = bs[pR];
								}
							}
							else
							{
								if (x <= 0)
								{
									top = bs[pT];
									left = byte.MinValue;
									bottom = bs[pB];
									right = bs[pR];
								}
								else if (x >= width - 1)
								{
									top = bs[pT];
									left = bs[pL];
									bottom = bs[pB];
									right = byte.MinValue;
								}
								else
								{
									top = bs[pT];
									left = bs[pL];
									bottom = bs[pB];
									right = bs[pR];
								}
							}
							v = 0;
							c = 0;
							value = bs[pM];
							if (top >= value)
							{
								v += top;
								c++;
							}
							if (bottom >= value)
							{
								v += bottom;
								c++;
							}
							if (left >= value)
							{
								v += left;
								c++;
							}
							if (right >= value)
							{
								v += right;
								c++;
							}
							if (c > 1)
								value = (byte)((v / c + value) / 2);
							bdp[pM] = value;
						}//i
					}//x
				}//y
			}//unsafe
			bitmap.UnlockBits(bdt);
			return bitmap;
		}

		/// <summary>
		/// 锐化
		/// </summary>
		/// <param name="bitmap">要处理的图片</param>
		/// <param name="amount">数量</param>
		/// <returns>锐化后图片</returns>
		public static Bitmap Sharpening(Bitmap bitmap, byte amount)
		{
			var pC = 0;
			var pP = 0;
			var v = 0;

			var width = bitmap.Width;
			var height = bitmap.Height;
			var bdt = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, bitmap.PixelFormat);
			var stride = bdt.Stride;
			var formatSize = stride / width;
			unsafe
			{
				var bdp = (byte*)bdt.Scan0.ToPointer();
				for (var y = 0; y < height; y++)
				{
					for (var x = 0; x < width; x++)
					{
						pC = y * stride + x * formatSize;
						if (y == 0 && x == 0)
							pP = (y + 1) * stride + (x + 1) * formatSize;
						else if (y == 0)
							pP = (y + 1) * stride + (x - 1) * formatSize;
						else if (x == 0)
							pP = (y - 1) * stride + (x + 1) * formatSize;
						for (var i = 0; i < formatSize; i++)
						{
							v = bdp[pC + i] - bdp[pP + i];
							v = v * amount / byte.MaxValue;
							v = bdp[pC + i] + v;
							if (v > byte.MaxValue)
								v = byte.MaxValue;
							if (v < byte.MinValue)
								v = byte.MinValue;
							bdp[pC] = (byte)v;
						}//i
					}//x
				}//y
			}//unsafe
			bitmap.UnlockBits(bdt);
			return bitmap;
		}

		/// <summary>
		/// USM锐化
		/// </summary>
		/// <param name="bitmap">要处理的图片</param>
		/// <param name="amount">数量</param>
		/// <param name="radius">半径</param>
		/// <param name="threshold">阈值</param>
		/// <returns>USM锐化后图片</returns>
		public static Bitmap UnsharpMask(Bitmap bitmap, byte amount, int radius, int threshold)
		{
			var pC = 0;
			var pP = 0;
			var v = 0;

			var width = bitmap.Width;
			var height = bitmap.Height;
			var bdt = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, bitmap.PixelFormat);
			var stride = bdt.Stride;
			var formatSize = stride / width;
			var ps = COcr.GetRadius(radius);
			unsafe
			{
				var bdp = (byte*)bdt.Scan0.ToPointer();
				for (var bit = 0; bit < formatSize; bit++)
				{
					for (var y = 0; y < height; y++)
					{
						for (var x = 0; x < width; x++)
						{
							pC = y * stride + x * formatSize;
							for (var i = 0; i < ps.Length; i++)
							{
								var iY = y + ps[i].Y;
								var iX = x + ps[i].X;
								if (iY < 0 || iX < 0 || iY >= height || iX >= width)
									continue;

								pP = iY * stride + iX * formatSize;
								v = bdp[pP] - bdp[pC];
								if (v >= -threshold && v <= threshold)
									continue;

								v = v * amount / byte.MaxValue;
								v = bdp[pP] + v;
								if (v > byte.MaxValue)
									v = byte.MaxValue;
								if (v < byte.MinValue)
									v = byte.MinValue;
								bdp[pP] = (byte)v;
							}//i
						}//x
					}//y
				}//bit
			}//unsafe
			bitmap.UnlockBits(bdt);
			return bitmap;
		}

		/// <summary>
		/// 查找边界
		/// </summary>
		/// <param name="bitmap">要处理的图片</param>
		/// <param name="threshold">阈值</param>
		/// <param name="amount">数量</param>
		/// <returns>存有边界的图片</returns>
		public static Bitmap FindEdge(Bitmap bitmap, byte threshold, int amount)
		{
			int p, px, py;
			var b = false;
			var w = bitmap.Width;
			var h = bitmap.Height;
			var bmp = new Bitmap(1, 1, PixelFormat.Format8bppIndexed);
			var palette = bmp.Palette;
			bmp.Dispose();
			bmp = new Bitmap(w, h, PixelFormat.Format8bppIndexed);
			var bdt = bmp.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.WriteOnly, bmp.PixelFormat);
			if (bitmap.PixelFormat == PixelFormat.Format32bppRgb)	//
				bitmap = new Bitmap(bitmap);						//
			var bitmapData = bitmap.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadOnly, bitmap.PixelFormat);
			var bdtS = Math.Abs(bdt.Stride);
			var bitmapDataS = Math.Abs(bitmapData.Stride);
			var bdtW = bdtS / w;
			var bitmapDataW = bitmapDataS / w;
			for (var i = 0; i < palette.Entries.Length; i++)
				palette.Entries[i] = Color.FromArgb(i, i, i);
			bmp.Palette = palette;
			if (amount == 0)
				amount = 1;
			else
				amount = Math.Abs(amount);
			unsafe
			{
				var bdtP = (byte*)bdt.Scan0.ToPointer();
				var bitmapDataP = (byte*)bitmapData.Scan0.ToPointer();
				for (var y = 0; y < h; y++)
				{
					for (var x = 0; x < w; x++)
					{
						b = false;
						p = y * bitmapDataS + x * bitmapDataW;
						if (y + amount < h)
							py = (y + amount) * bitmapDataS + x * bitmapDataW;
						else
							py = p;
						if (x + amount < w)
							px = y * bitmapDataS + (x + amount) * bitmapDataW;
						else
							px = p;
						for (var i = 0; i < bitmapDataW; i++)
						{
							//if ((p == px || bitmapDataP[p + i] - bitmapDataP[px + i] >= threshold || bitmapDataP[px + i] - bitmapDataP[p + i] >= threshold) &&
							//    (p == py || bitmapDataP[p + i] - bitmapDataP[py + i] >= threshold || bitmapDataP[py + i] - bitmapDataP[p + i] >= threshold))
							//{
							//    b = true;
							//    break;
							//}
							if (bitmapDataP[p + i] - bitmapDataP[px + i] >= threshold ||
								bitmapDataP[px + i] - bitmapDataP[p + i] >= threshold ||
								bitmapDataP[p + i] - bitmapDataP[py + i] >= threshold ||
								bitmapDataP[py + i] - bitmapDataP[p + i] >= threshold)
							{
								b = true;
								break;
							}
						}
						if (b && amount > 1)
						{
							for (var ii = 2; ii <= amount; ii++)
							{
								if (y + ii < h)
									py = (y + ii) * bitmapDataS + x * bitmapDataW;
								else
									py = p;
								if (x + ii < w)
									px = y * bitmapDataS + (x + ii) * bitmapDataW;
								else
									px = p;
								for (var i = 0; i < bitmapDataW; i++)
								{
									//if ((p == px || bitmapDataP[p + i] - bitmapDataP[px + i] >= threshold || bitmapDataP[px + i] - bitmapDataP[p + i] >= threshold) &&
									//    (p == py || bitmapDataP[p + i] - bitmapDataP[py + i] >= threshold || bitmapDataP[py + i] - bitmapDataP[p + i] >= threshold))
									//{
									//    b = true;
									//    break;
									//}
									if (bitmapDataP[p + i] - bitmapDataP[px + i] >= threshold ||
										bitmapDataP[px + i] - bitmapDataP[p + i] >= threshold ||
										bitmapDataP[p + i] - bitmapDataP[py + i] >= threshold ||
										bitmapDataP[py + i] - bitmapDataP[p + i] >= threshold)
									{
										b = true;
										break;
									}
								}
								if (!b)
									break;
							}
						}
						p = y * bdtS + x * bdtW;
						for (var i = 0; i < bdtW; i++)
							bdtP[p + i] = b ? byte.MaxValue : byte.MinValue;
					}
				}
			}
			bmp.UnlockBits(bdt);
			bitmap.UnlockBits(bitmapData);
			return bmp;
		}

		/// <summary>
		/// 图片反色
		/// </summary>
		/// <param name="bitmap">要处理的图片</param>
		/// <returns>反色后的图片</returns>
		public static Bitmap ReverseColor(Bitmap bitmap)
		{
			if (bitmap.PixelFormat == PixelFormat.Format32bppRgb)	//
				bitmap = new Bitmap(bitmap);						//
			var bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat);
			var l = Math.Abs(bitmapData.Stride * bitmap.Height);
			unsafe
			{
				var bitmapDataP = (byte*)bitmapData.Scan0.ToPointer();
				for (var i = 0; i < l; i++)
					bitmapDataP[i] = (byte)(byte.MaxValue - bitmapDataP[i]);
			}
			bitmap.UnlockBits(bitmapData);
			return bitmap;
		}

		/// <summary>
		/// 图片极化（二极化）
		/// </summary>
		/// <param name="bitmap">要处理的图片</param>
		/// <returns>极化后的图片</returns>
		public static Bitmap Polarize(Bitmap bitmap, byte threshold)
		{
			if (bitmap.PixelFormat == PixelFormat.Format32bppRgb)	//
				bitmap = new Bitmap(bitmap);						//
			var bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat);
			var l = Math.Abs(bitmapData.Stride * bitmap.Height);
			unsafe
			{
				var bitmapDataP = (byte*)bitmapData.Scan0.ToPointer();
				for (var i = 0; i < l; i++)
				{
					if (bitmapDataP[i] > threshold)
						bitmapDataP[i] = byte.MaxValue;
					else
						bitmapDataP[i] = byte.MinValue;
				}
			}
			bitmap.UnlockBits(bitmapData);
			return bitmap;
		}

		private static Point[] GetRadius(int radius)
		{
			var ls = new List<Point>();
			for (var x = -radius; x <= radius; x++)
			{
				for (var y = -radius; y <= radius; y++)
				{
					var v = Math.Sqrt(Math.Pow(x, 2) + Math.Pow(y, 2));
					if (radius >= v)
						ls.Add(new Point(x, y));
				}
			}
			return ls.ToArray();
		}
		/*
		public static Bitmap Contrast(Bitmap bitmap)
		{
			if (bitmap.PixelFormat != PixelFormat.Format8bppIndexed)
				bitmap = COcr.Gray(bitmap);
			var value = byte.MinValue;
			var position = 0;
			var w = bitmap.Width;
			var h = bitmap.Height;
			var s = Image.GetPixelFormatSize(PixelFormat.Format8bppIndexed);
			var bmp = new Bitmap(w, h, PixelFormat.Format8bppIndexed);
			bmp.Palette = bitmap.Palette;
			var bdtN = bmp.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.WriteOnly, bmp.PixelFormat);
			var bdtO = bitmap.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadOnly, bitmap.PixelFormat);
			unsafe
			{
				var bdpN = (byte*)bdtN.Scan0.ToPointer();
				var bdpO = (byte*)bdtO.Scan0.ToPointer();
				for (var y = 0; y < h; y++)
				{
					for (var x = 0; x < w; x++)
					{
						position = y * w * s + x * s + 1;
						value = (byte)(.299 * bdpO[position + 1] + .587 * bdpO[position] + .114 * bdpO[position - 1]);//RGB -> GDI+:BGR
						bdpN[y * w * s + x * s] = value;
					}
				}
			}
			bmp.UnlockBits(bdtN);
			bitmap.UnlockBits(bdtO);
			return bmp;
		}
		*/
	}
}
